﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Sherwood.SignOn.Server.Models.Repositories;
using Sherwood.SignOn;

namespace Sherwood.SignOn.Server.Models.Repositories.InMemory
{
    /// <summary>
    /// Generic implementation of the UserAccount repository
    /// </summary>
    public class UserAccountRepository : IUserAccountRepository
    {
        static UserAccountRepository()
        {
        }

        public UserAccountRepository()
        {

        }

        //In-memory useraccount store
        private static List<UserAccount> _all = new List<UserAccount>();

        /// <summary>
        /// Returns all user accounts
        /// </summary>
        public virtual List<UserAccount> All
        {
            get
            {
                return _all;                
            }
        }

        #region IUserAccountRepository Members
        

        /// <summary>
        /// Provides user authentication using username and password
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        /// <returns>UserAccount object if user is successfully authenticated. Null if user if authentication failed.</returns>
        public virtual IUserAccount AuthenticateUser(string username, string password)
        {
            username = username == null ? "" : username.Trim().ToLower();
            IUserAccount user = (from a in All where (a.IsEnabled == true && a.UserName != null && a.UserName.ToLower() == username) select a).FirstOrDefault();
            if (user != null)
            {
                if (user.VerifyPassword(password))
                {
                    return user;
                }
            }
            return null;
        }

        /// <summary>
        /// Get user account object for the specified userAccountId
        /// </summary>
        /// <param name="userAccountId"></param>
        /// <returns>Returns UserAccount object if found, otherwise null.</returns>
        public virtual IUserAccount GetUserAccount(Guid userAccountId)
        {
            return (from a in All where a.Id == userAccountId select a).FirstOrDefault();
        }


        public virtual bool IsStrongPassword(string password, string username)
        {
            return password != null && username != null && !password.Equals(username, StringComparison.OrdinalIgnoreCase) && password.Length > 3;
        }



        public virtual IUserAccount CreateUserAccount(CreateUserAccountParams parameters)
        {
            UserAccount account = null;
            if (parameters.UserAccountId != Guid.Empty)
            {
                account = GetUserAccount(parameters.UserAccountId) as UserAccount;
            }
            bool isNewAccount = account == null;
            if (account == null)
            {
                account = new UserAccount();
                
            }

            account.RequestedUrl = parameters.RequestedUrl;
            account.DateCreated = DateTime.Now;
            account.DateModified = DateTime.Now;
            account.UserName = parameters.UserName;
            account.DateOfBirth = parameters.DateOfBirth;
            account.Email = parameters.Email;
            account.Mobile = parameters.Mobile;
            account.Gender = parameters.Gender;
            account.CultureName1 = parameters.CultureName1;
            account.CultureName2 = parameters.CultureName2;
            account.CultureName3 = parameters.CultureName3;
            account.Country = parameters.Country;
            account.FirstName = parameters.FirstName;
            account.LastName = parameters.LastName;
            account.IsEnabled = parameters.IsEnabled;
            if (isNewAccount)
            {
                account.Id = Guid.NewGuid();
                account.CreateVerificationCode();
                All.Add(account);
            }
            account.VerificationStatus = parameters.VerificationStatus;
            account.SetPassword(parameters.Password);

            return account;

        }



        /// <summary>
        /// Modifies an existing user account using the passed in parameters and
        /// returns the account.
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns>Modified account</returns>
        public virtual IUserAccount ModifyUserAccount(CreateUserAccountParams parameters)
        {
            UserAccount account = null;
            if (parameters.UserAccountId != Guid.Empty)
            {
                account = GetUserAccount(parameters.UserAccountId) as UserAccount;
            }
            if (account == null)
            {
                return null;
            }

            account.RequestedUrl = parameters.RequestedUrl;
            //account.DateCreated = parameters.DateCreated;
            account.DateModified = DateTime.Now;
            account.UserName = parameters.UserName;
            account.DateOfBirth = parameters.DateOfBirth;
            //account.Email = parameters.Email; (email address changes not allowed without verification)
            account.Mobile = parameters.Mobile;
            account.Gender = parameters.Gender;
            account.CultureName1 = parameters.CultureName1;
            account.CultureName2 = parameters.CultureName2;
            account.CultureName3 = parameters.CultureName3;
            account.Country = parameters.Country;
            account.FirstName = parameters.FirstName;
            account.LastName = parameters.LastName;
            return account;
        }



        public virtual IUserAccount GetUserAccountByEmail(string email)
        {
            if (!string.IsNullOrEmpty(email))
            {
                return (from a in All where a.Email != null && a.Email.Equals(email,StringComparison.OrdinalIgnoreCase) select a).FirstOrDefault();
            }
            return null;
        }

        public virtual List<IUserAccount> GetAllUserAccountsByEmail(string email)
        {
            if (!string.IsNullOrEmpty(email))
            {
                List<UserAccount> accounts = (from a in All where a.Email != null && a.Email.ToLower() == email select a).ToList();
                return new List<IUserAccount>(accounts);
            }
            return null;
        }


        public virtual IUserAccount GetUserAccountByUsername(string username)
        {
            if (!string.IsNullOrEmpty(username))
            {
                return (from a in All where a.UserName != null && a.UserName.Equals(username, StringComparison.OrdinalIgnoreCase) select a).FirstOrDefault();
            }
            return null;
        }


        public virtual IUserAccount GetUserAccountByUsernameAndEmail(string username, string email)
        {
            if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(email))
            {
                return (from a in All where a.UserName != null && a.UserName.Equals(username,StringComparison.OrdinalIgnoreCase) && a.Email != null && a.Email.Equals(email, StringComparison.OrdinalIgnoreCase) select a).FirstOrDefault();
            }
            return null;
        }


        public virtual IUserAccount GetUserAccountByUsernameOrEmail(string username, string email)
        {
            if (!string.IsNullOrEmpty(username) || !string.IsNullOrEmpty(email))
            {
                return (from u in All where (!String.IsNullOrEmpty(u.Email) && u.Email.Equals(email, StringComparison.OrdinalIgnoreCase)) || (!String.IsNullOrEmpty(u.UserName) && u.UserName.Equals(username, StringComparison.OrdinalIgnoreCase)) select u).FirstOrDefault();
            }
            return null;
        }


        /// <summary>
        /// Can be overridden to allow duplicate users to be created in certain instances.
        /// <param name="parameters"></param>
        /// <returns></returns>
        public virtual bool AllowDuplicateEmailAddress(CreateUserAccountParams parameters)
        {
            return false;
        }
        

        #endregion



        public IEnumerable<IUserAccount> GetAllAccounts()
        {
            return _all;
        }

        public void DeleteAllAccounts()
        {
            _all.Clear();
        }


        public IEnumerable<IUserAccount> GetUserAccounts(string search)
        {
            search = search != null ? search.Replace(",","").Replace("\n"," ").Replace("\r","").Replace("\t", " ").Trim().ToLower() : "";
            if (string.IsNullOrEmpty(search))
                return new List<IUserAccount>(); //return empty list

            if (!search.Contains(" "))
            {
                return (from a in _all where a.UserName.ToLower().Contains(search) || a.Email.ToLower().Contains(search) || a.FirstName.ToLower().Contains(search) || a.LastName.ToLower().Contains(search) select a).AsEnumerable();
            }
            else
            {
                string[] parts = search.Split(' ');
                string[] simpleParts = new string[parts.Length];
                for (int i=0; i<parts.Length; i ++)
                    simpleParts[i] = StringHelper.RemoveDiacritics(parts[i]);

                Dictionary<Guid,IUserAccount> resultList = new Dictionary<Guid,IUserAccount>();
                foreach (string part in simpleParts)
                {
                    if (part != "")
                    {
                        //Coarse filter based on one one segment of a space seperated search string
                        IEnumerable<IUserAccount> partlist = (from a in _all where a.UserName.ToLower().Contains(part) || a.Email.ToLower().Contains(part) || a.FirstName.ToLower().Contains(part) || a.LastName.ToLower().Contains(part) select a).AsEnumerable();

                        //Refine result list by matching segements with multiple fields
                        foreach (IUserAccount user in partlist)
                        {
                            string userFirstName = StringHelper.RemoveDiacritics(user.FirstName).ToLower().Trim();
                            string userLastName = StringHelper.RemoveDiacritics(user.LastName).ToLower().Trim();
                            string userEmail = StringHelper.RemoveDiacritics(user.Email).ToLower().Trim();

                            if ((userFirstName.Contains(simpleParts[0]) && userLastName.Contains(simpleParts[1]))
                                || (userFirstName.Contains(simpleParts[1]) && userLastName.Contains(simpleParts[0]))
                                || (parts.Length > 2 && userFirstName.Contains(simpleParts[0]) && userLastName.Contains(simpleParts[simpleParts.Length - 1]))
                                || (parts.Length > 2 && userFirstName.Contains(simpleParts[simpleParts.Length - 1]) && userLastName.Contains(simpleParts[0]))
                                || (userEmail.Contains(simpleParts[0]) && userEmail.Contains(simpleParts[1]))
                                || (userEmail.Contains(simpleParts[0]) && userLastName.Contains(simpleParts[1]))
                                || (userEmail.Contains(simpleParts[1]) && userLastName.Contains(simpleParts[0]))
                                || StringHelper.LooseMatch(search, user.FirstName + user.LastName)
                                || StringHelper.LooseMatch(search, user.LastName + user.FirstName)
                                )
                            {
                                if (!resultList.ContainsKey(user.Id))
                                    resultList.Add(user.Id, user);
                            }
                        }
                    }
                }
                List<IUserAccount> sortedResults = resultList.Values.ToList();
                sortedResults.Sort((a, b) => (a.LastName + a.FirstName).CompareTo(b.LastName + a.FirstName));
                return sortedResults;
            }
        }


        public bool DeleteUserAccount(Guid userAccountId)
        {
            UserAccount account = (from a in _all where a.Id.Equals(userAccountId) select a).FirstOrDefault();
            if (account == null)
            {
                return false;
            }
            else
            {
                _all.Remove(account);
                return true;
            }
        }
    }
}
